package com.clp.protocol.iec104.iec104.client.state;

import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 抽象可激活状态：在激活时会进行状态重置；重新激活会进行状态恢复；停止激活会进行状态重置（暂时没有这么做）
 */
@Slf4j
public abstract class AbstractActivatableState implements ActivatableState<Future<Void>> {
    /**
     * 定时任务执行器
     */
    private final ScheduledExecutorService scheduledTaskRunner;
    /**
     * 定时任务future
     */
    private volatile ScheduledFuture<?> scheduledFuture;
    private volatile ScheduledTask task;

    protected AbstractActivatableState(ScheduledExecutorService scheduledTaskRunner) {
        this.scheduledTaskRunner = scheduledTaskRunner;
    }

    protected abstract  <T> Promise<T> createPromise(Class<T> clazz);

    @Override
    public synchronized Future<Void> activate() {
        if (isActivated()) {
            return createPromise(Void.class).setFailure(new IllegalStateException("State already activated"));
        }
        activate0();
        Promise<Void> promise = createPromise(Void.class);
        resetState().addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {
                    promise.setSuccess(null);
                    return;
                }
                Throwable cause = future.cause();
                inactivate().addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        promise.setFailure(cause);
                    }
                });
            }
        });
        return promise;
    }

    private void activate0() {
        if (task == null) {
            task = getScheduledTask();
        }
        scheduledFuture = scheduledTaskRunner.scheduleAtFixedRate(task, task.initialDelay, task.period, task.timeUnit);
    }

    /**
     * 在激活之前会重置状态
     */
    protected abstract Future<Void> resetState();

    protected abstract ScheduledTask getScheduledTask();

    @Override
    public boolean isActivated() {
        return scheduledFuture != null && !scheduledFuture.isCancelled();
    }

    @Override
    public Future<Void> reactivate() {
        if (isActivated()) {
            log.warn("已经激活，无法再次激活！");
            return createPromise(Void.class).setFailure(new IllegalStateException("State already activated"));
        }
        activate0();
        Promise<Void> promise = createPromise(Void.class);
        refreshState().addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {
                    promise.setSuccess(null);
                    return;
                }
                Throwable cause = future.cause();
                inactivate().addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        promise.setFailure(cause);
                    }
                });
            }
        });
        return promise;
    }

    /**
     * 在重新激活之前会刷新状态
     */
    protected abstract Future<Void> refreshState();

    @Override
    public synchronized Future<Void> inactivate() {
        Promise<Void> promise = createPromise(Void.class);
        if (isActivated() && !scheduledFuture.cancel(true)) {
            log.warn("任务停止失败！");
            return promise.setFailure(new IllegalStateException("任务停止失败！"));
        }
        scheduledFuture = null;
        promise.setSuccess(null);
        return promise;
    }

    protected static abstract class ScheduledTask implements Runnable {
        private final long initialDelay;
        private final long period;
        private final TimeUnit timeUnit;
        public ScheduledTask(long initialDelay, long period, TimeUnit timeUnit) {
            this.initialDelay = initialDelay;
            this.period = period;
            this.timeUnit = timeUnit;
        }
    }
}
